﻿using System;
using System.Collections.Generic;
using CrmClient.Mapping;
using CrmClient.Query;
using CrmClient.QueryConvert;
using Microsoft.Xrm.Sdk.Query;
using System.Threading.Tasks;
using Microsoft.Xrm.Sdk;
using System.Collections.ObjectModel;
using Microsoft.Xrm.Sdk.Messages;
using Microsoft.Xrm.Sdk.Metadata;

namespace CrmClient
{
    public abstract class CrmClientBase : IDisposable
    {
        private bool _disposed;

        protected abstract IWcfCrmClient WcfClient { get; }

        public void Dispose()
        {
            WcfClient.Close();
            _disposed = true;
        }

        public T OptionsSet<T>()
            where T : CrmOptionsSetBase, new()
        {
            var r = new OrganizationRequest("RetrieveOptionSet");
            r.Parameters.Add("Name", MappingUtils.GetCrmEntityName(typeof(T)));
            r.Parameters.Add("RetrieveAsIfPublished", false);
            r.Parameters.Add("MetadataId", Guid.Empty);
            var response = WcfClient.Execute(r);
            T res = new T();
            foreach(var result in response.Results)
                foreach (OptionMetadata option in ((OptionSetMetadata)result.Value).Options)
                    res.Add(new CrmOption(option.Value, option.Label.UserLocalizedLabel.Label));
            return res;
        }

        public CrmQueryable<T> Query<T>()
            where T : CrmDataContractBase
        {
            var convertor = QueryConvertorFactory.GetConvertor<T>();
            return new CrmQueryable<T>(this, convertor.PropertiesMap, convertor.EntityName);
        }

        public void Add<T>(T item)
            where T : CrmDataContractBase
        {
            var convertor = QueryConvertorFactory.GetConvertor<T>();
            var entity = convertor.CreateEntity(item, null);
            item.Id = WcfClient.Create(entity);
        }

        public void Delete<T>(T item)
             where T : CrmDataContractBase
        {
            var convertor = QueryConvertorFactory.GetConvertor<T>();
            WcfClient.Delete(convertor.EntityName, item.Id);
        }

        public void Update<T>(T item)
             where T : CrmDataContractBase
        {
            var entity = QueryConvertorFactory.GetConvertor<T>().CreateEntity(item, item.Id);
            WcfClient.Update(entity);
        }

        internal IEnumerable<T> ExecureQuery<T>(QueryExpression query, PropertiesMap propertiesMap, PagedQueryInfo pagedQueryInfo)
        {
            if (_disposed)
                throw new InvalidOperationException("Нельзя выполнить запрос, так как соединение с сервером закрыто. Возможно причина в том, что вызов этого метода происходит за пределами using клиента.");
            
            var convertor = QueryConvertorFactory.GetConvertor<T>(propertiesMap, query.EntityName);
            var requestResult = WcfClient.RetrieveMultiple(query);
            var list = new T[requestResult.Entities.Count];
            Parallel.ForEach(requestResult.Entities, (entity, state, index) => list[index] = convertor.CreateItem(entity));
            if (pagedQueryInfo != null)
            {
                pagedQueryInfo.TotalRecordCount = requestResult.TotalRecordCount;
                pagedQueryInfo.MoreRecords = requestResult.MoreRecords;
                pagedQueryInfo.PagingCookie = requestResult.PagingCookie;
            }
            return list;
        }
    }
}
